<template>
  <div class="webgl-color-ring">
    <h1>WEBGL彩色旋转光圈</h1>
    <p>使用WebGL实现彩色旋转光圈效果，展示如何使用片元着色器创建动态旋转的彩色光环。</p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>彩色光圈展示</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="demoCanvas" width="600" height="400"></canvas>
        <div class="control-panel">
          <el-form :model="ringForm" label-width="100px">
            <el-form-item label="旋转速度">
              <el-slider
                v-model="ringForm.speed"
                :min="0.5"
                :max="5.0"
                :step="0.5"
                @change="updateSpeed"
              />
            </el-form-item>
          </el-form>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>着色器代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code class="glsl">
  // 顶点着色器
  attribute vec4 a_Position;
  void main() {
    gl_Position = a_Position;
  }
        </code></pre>
        <pre><code class="glsl">
  // 片元着色器
  precision mediump float;
  uniform vec2 u_Resolution;
  uniform float u_Time;
  uniform float u_Speed;

  void main() {
    // 坐标预处理
    vec2 uv = gl_FragCoord.xy/u_Resolution.xy;
    vec2 p = uv - 0.5;
    p.x *= u_Resolution.x/u_Resolution.y;

    float angle = atan(p.y, p.x);
    float rotationOffset = u_Time * u_Speed;
    float rotatedAngle = angle + rotationOffset;

    float dist = length(p);

    // 优化光环过渡效果，移除黑边
    float mainRing = smoothstep(0.18, 0.185, dist) * smoothstep(0.215, 0.20, dist);

    // 增强内部光晕，使过渡更自然
    float innerGlow = smoothstep(0.1, 0.18, dist) * smoothstep(0.185, 0.18, dist) * 0.7;

    // 扩大外部光晕范围，柔和过渡
    float outerGlow = smoothstep(0.22, 0.215, dist) * smoothstep(0.215, 0.25, dist) * 0.6;

    // 添加额外的柔和光晕层
    float softGlow = smoothstep(0.0, 0.35, dist) * smoothstep(0.4, 0.35, dist) * 0.3;

    // 组合所有光环效果，确保平滑过渡
    float ring = mainRing + innerGlow + outerGlow + softGlow;

    // 生成更柔和的彩色效果
    vec3 color;
    color.r = 0.5 + 0.5 * sin(rotatedAngle * 2.0);
    color.g = 0.5 + 0.5 * sin(rotatedAngle * 2.0 + 2.094);
    color.b = 0.5 + 0.5 * sin(rotatedAngle * 2.0 + 4.189);

    // 调整颜色亮度和饱和度
    color = pow(color * 1.3, vec3(1.1));

    // 添加更柔和的光晕效果
    vec3 glowColor = color * vec3(0.85, 0.95, 1.0);
    vec3 finalColor = mix(color, glowColor, innerGlow + outerGlow + softGlow);

    // 最终输出，使用平方函数优化透明度过渡
    float alpha = ring * ring;  // 平方处理使边缘更加柔和
    gl_FragColor = vec4(finalColor * alpha, alpha);
  }
        </code></pre>
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, reactive } from 'vue'
import { WebGLUtils } from '@/utils/webgl'

const demoCanvas = ref<HTMLCanvasElement | null>(null)
let gl: WebGLRenderingContext | null = null
let program: WebGLProgram | null = null
// 创建WebGL工具实例
const webglUtils = ref<WebGLUtils | null>(null)
// 表单数据
const ringForm = reactive({
  speed: 2.0
})

// 着色器源代码
const vertexShaderSource = `
  attribute vec4 a_Position;
  void main() {
    gl_Position = a_Position;
  }
`

const fragmentShaderSource = `
  precision mediump float;
  uniform vec2 u_Resolution;
  uniform float u_Time;
  uniform float u_Speed;

  void main() {
    // 坐标预处理
    vec2 uv = gl_FragCoord.xy/u_Resolution.xy;
    vec2 p = uv - 0.5;
    p.x *= u_Resolution.x/u_Resolution.y;

    float angle = atan(p.y, p.x);
    float rotationOffset = u_Time * u_Speed;
    float rotatedAngle = angle + rotationOffset;

    float dist = length(p);

    // 光环效果
    float mainRing = smoothstep(0.18, 0.185, dist) * smoothstep(0.215, 0.20, dist);

    // 增强内部光晕，使过渡更自然
    float innerGlow = smoothstep(0.1, 0.18, dist) * smoothstep(0.185, 0.18, dist) * 0.7;

    // 扩大外部光晕范围，柔和过渡
    float outerGlow = smoothstep(0.22, 0.215, dist) * smoothstep(0.215, 0.25, dist) * 0.6;

    // 组合所有光环效果，确保平滑过渡
    float ring = mainRing + innerGlow + outerGlow;

    // 生成更柔和的彩色效果
    vec3 color;
    color.r = 0.5 + 0.5 * sin(rotatedAngle * 2.0);
    color.g = 0.5 + 0.5 * sin(rotatedAngle * 2.0 + 2.094);
    color.b = 0.5 + 0.5 * sin(rotatedAngle * 2.0 + 4.189);

    // 调整颜色亮度和饱和度
    color = pow(color, vec3(1.3));

    // 最终输出，使用平方函数优化透明度过渡
    float alpha = ring * ring;  // 平方处理使边缘更加柔和
    gl_FragColor = vec4(color * alpha, alpha);
  }
`

// 初始化顶点缓冲区
function initVertexBuffers(gl: WebGLRenderingContext, program: WebGLProgram) {
  // 创建覆盖整个canvas的矩形
  const vertices = new Float32Array([
    -1.0, 1.0,
    -1.0, -1.0,
    1.0, 1.0,
    1.0, -1.0
  ])

  // 创建缓冲区
  const vertexBuffer = gl.createBuffer()
  if (!vertexBuffer) {
    console.error('Failed to create buffer')
    return -1
  }

  // 绑定缓冲区
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
  // 写入数据
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

  // 获取attribute变量地址
  const a_Position = gl.getAttribLocation(program, 'a_Position')
  if (a_Position < 0) {
    console.error('Failed to get attribute location')
    return -1
  }

  // 将缓冲区对象分配给attribute变量
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)
  // 启用attribute变量
  gl.enableVertexAttribArray(a_Position)

  return vertices.length / 2
}

// 更新速度
function updateSpeed() {
  if (gl && program) {
    const u_Speed = gl.getUniformLocation(program, 'u_Speed')
    gl.uniform1f(u_Speed, ringForm.speed)
  }
}

// 渲染场景
function render() {
  if (!gl || !program || !demoCanvas.value) return

  // 设置视口
  gl.viewport(0, 0, demoCanvas.value.width, demoCanvas.value.height)

  // 设置清除颜色和清除缓冲区
  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.clear(gl.COLOR_BUFFER_BIT)

  // 设置分辨率
  const u_Resolution = gl.getUniformLocation(program, 'u_Resolution')
  gl.uniform2f(u_Resolution, demoCanvas.value.width, demoCanvas.value.height)

  // 设置时间（使用性能计时器获取更精确的时间）
  const currentTime = performance.now() * 0.001 // 转换为秒
  const u_Time = gl.getUniformLocation(program, 'u_Time')
  gl.uniform1f(u_Time, currentTime)

  // 设置速度
  const u_Speed = gl.getUniformLocation(program, 'u_Speed')
  gl.uniform1f(u_Speed, ringForm.speed)

  // 绘制矩形
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)

  // 请求下一帧
  requestAnimationFrame(render)
}

// 初始化WebGL
function initWebGL() {
  if (!demoCanvas.value) return

  // 获取WebGL上下文
  gl = demoCanvas.value.getContext('webgl')
  if (!gl) {
    console.error('Failed to get WebGL context')
    return
  }

  // 创建WebGL工具实例
  webglUtils.value = new WebGLUtils(gl)

  const vertexShader = webglUtils.value.createShader(gl.VERTEX_SHADER, vertexShaderSource)
  const fragmentShader = webglUtils.value.createShader(gl.FRAGMENT_SHADER, fragmentShaderSource)
  if (!vertexShader || !fragmentShader) {
    console.error('Failed to create shaders')
    return
  }

  // 创建着色器程序
  program = webglUtils.value.createProgram(vertexShader, fragmentShader)
  if (!program) {
    console.error('Failed to create program')
    return
  }

  // 使用程序
  gl.useProgram(program)

  // 启用混合
  gl.enable(gl.BLEND)
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

  // 初始化顶点缓冲区
  const n = initVertexBuffers(gl, program)
  if (n < 0) {
    console.error('Failed to initialize vertex buffers')
    return
  }

  // 开始渲染
  render()
}

// 组件挂载时初始化WebGL
onMounted(() => {
  initWebGL()
})
</script>

<style lang="scss" scoped>
.webgl-color-ring {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;

  h1 {
    color: #303133;
    margin-bottom: 20px;
  }

  p {
    margin-bottom: 30px;
  }

  .demo-section,
  .code-section {
    margin: 30px 0;

    .card-header {
      display: flex;
      align-items: center;

      h3 {
        margin: 0;
        color: #409EFF;
      }
    }
  }

  .demo-section {
    .demo-content {
      display: flex;
      gap: 20px;
      justify-content: center;

      canvas {
        border: 1px solid #dcdfe6;
        background-color: #000;
      }

      .control-panel {
        flex: 1;
        padding: 20px;
      }
    }
  }

  .code-section {
    .code-content {
      background-color: #f8f9fa;
      padding: 15px;
      border-radius: 4px;
      max-height: 500px;
      overflow-y: auto;

      code {
        font-family: monospace;
        color: #409EFF;
      }

      pre {
        margin: 0;
        white-space: pre-wrap;
      }
    }
  }
}
</style>
